/*
 * Created on Jun 4, 2007
 *
 * To change the template for this generated file go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */
package it.unibo.deis.lia.mmhc.mpe;

import java.util.*;

import it.unibo.deis.lia.networkInterface.*;
import it.unibo.deis.lia.location.*;
import it.unibo.deis.lia.filters.*;
import it.unibo.deis.lia.filters.graphic.FunctionFrame;
import it.unibo.deis.lia.mmhc.nip.*;

public class MobilityEstimatorBluetooth extends Thread {

	private boolean debug = false;

	private boolean ok = true;
	private long sampleInterval = 500;

	private Bluetooth bluetooth;
	private MobilityEstimator mobilityEstimator;
	private Vector<LocationOss> jointVector = new Vector<LocationOss>();

	private float upperBound = (float) 0.60;
	private float lowerBound = (float) 0.15;// 0.40;
	private int harmonic = 1;

	protected MobilityEstimatorBluetooth(
			/* int bluetoothInterface, */MobilityEstimator mobilityEstimator)
			throws Exception {
		// this.bluetooth=new Bluetooth(bluetoothInterface);
		this.mobilityEstimator = mobilityEstimator;
		NetworkInterfaceProvider networkInterfaceProvider = NetworkInterfaceProvider
				.getInstance();
		Vector<GenericNetworkInterface> availableInterfaces = networkInterfaceProvider
				.getAvailableInterfaces();
		// TODO (opzionale) bisogna gestire la possibilita' che ci siano piu'
		// interfacce Bluetooth
		for (int i = 0; i < availableInterfaces.size(); i++) {
			if (availableInterfaces.elementAt(i) instanceof Bluetooth) {
				bluetooth = (Bluetooth) availableInterfaces.elementAt(i);
				break;
			}
		}
		if (bluetooth == null)
			throw new Exception("MobilityEstimatorBluetooth unavailable");
	}

	protected void stopMobilityEstimatorBluetooth() {
		ok = false;
	}

	public void run() {
		FilterManager filterManager = new FilterManager(new Fourier());
		filterManager.setFilterMemory(8);
		filterManager.setMinimizeValueSet(true);

		// frame to visualize RSSI trace (optional)
		int frameWidth = 900, frameHeight = 240;
		FunctionFrame frameRSSI = null;
		FunctionFrame filteredRSSI = null;
		if (debug) {
			frameRSSI = new FunctionFrame("test (actual)", filterManager
					.getActualDataDB(), -5, 15);
			frameRSSI.setBounds(10, 10, frameWidth, frameHeight);
			frameRSSI.setVisible(true);
			frameRSSI.toFront();

			filteredRSSI = new FunctionFrame("test (filtered)", filterManager
					.getFilteredDataDB(), -5, 15);
			filteredRSSI.setBounds(10, 10 + frameHeight, frameWidth,
					frameHeight);
			filteredRSSI.setVisible(true);
			filteredRSSI.toFront();
		}

		while (ok) {
			try {
				Vector<RemoteBluetooth> devices;
				// 1) RSSI gathering
				if (mobilityEstimator.getPhase() == MobilityEstimator.SEARCH) {
					/*
					 * Search frequent inquiry connect to & gather rssi from
					 * every remote device
					 */
					bluetooth.startMonitoringRemoteDevices();
					devices = bluetooth.getRemoteDevices();
				} else {// if(mobilityEstimator.getPhase()==MobilityEstimator.CONNECTED){
					/*
					 * Connected => no inquiry gather rssi only of connected
					 * devices
					 */
					bluetooth.stopMonitoringRemoteDevices();
					devices = bluetooth.getConnectedDevices();
				}
				// System.out.println("MobilityEstimatorBluetooth devices "+devices);

				// System.out.println("MobilityEstimatorBluetooth connected "+bluetooth.getConnectedDevices());

				Vector<NamedLocationOss> rssiList = bluetooth
						.getRSSIList(devices);
				// Vector<LocationOss> rssiList=bluetooth.getLQList(devices);
				// System.out.println("MobilityEstimatorBluetooth rssiList "+rssiList);

				// 2) db population & filtering
				filterManager.feedPredictor(rssiList);

				// 3) create new joint vector
				Vector<LocationOss> jointVectorTemp = new Vector<LocationOss>();
				Set<String> macs = filterManager.getFilteredDataDB().keySet();
				Iterator<String> macsIt = macs.iterator();
				// System.out.println("MobilityEstimator macs "+macs);
				while (macsIt.hasNext()) {
					String aMac = macsIt.next();
					Vector<Double> aSequence = filterManager
							.getFilteredDataDB().get(aMac);
					try {
						// 3a) First Harmonic max module computation
						// System.out.println("MobilityEstimator aSequence "+aSequence+"    harmonic "+harmonic);
						float module = getModule(aSequence, harmonic);
						// System.out.println("MobilityEstimator module "+module);

						// 3b) Joint computation
						float joint;
						if (module == -1)
							joint = -1;
						else if (module <= lowerBound)
							joint = 1;
						else if (module >= upperBound)
							joint = 0;
						/* (module>lowerBound && module<upperBound) */
						else
							joint = (1 / (lowerBound - upperBound)) * module
									- (upperBound / (lowerBound - upperBound));
						jointVectorTemp.addElement(new LocationOss(aMac, ""
								+ joint));
					} catch (Exception e) {
						// do nothing
						// e.printStackTrace(System.out);
					}
				}
				jointVector = jointVectorTemp;

				// Iteration finished

				if (debug) {
					// RSSI draw
					frameRSSI.setTitle("Test (actual) - current AP: ???");// +
																			// currentAPaddress);
					frameRSSI.draw();
					filteredRSSI.setTitle("Test (filtered) - current AP: ???");// +
																				// currentAPaddress);
					filteredRSSI.draw();
				}

				sleep(sampleInterval);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/*
	 * getJoint() immediately provides monitored peer set and related Joint
	 * estimations (the result strongly depends on current search/connected
	 * state)
	 */
	protected Vector<LocationOss> getJoint() {
		return jointVector;
	}

	private float getModule(Vector<Double> valueSet, int harmonic)
			throws Exception {
		int setSize = valueSet.size();
		// System.out.println("MobilityEstimator setSize "+setSize);

		// int pmax=harmonic*4;
		// int exploitedValues=4;
		int exploitedValues = setSize;
		// if(valueSet.size()<pmax) throw new
		// Exception("Unable to compute Fourier Coefficients");

		for (int p = 0; p < setSize / 2 + 1; p++) {// at most N/2 fourier
													// coefficients
			double tempA = 0;
			double tempB = 0;
			// int max=/*N*/pmax;
			for (int n = 0; n < exploitedValues/* pmax */; n++) {
				tempA = tempA
						+ 2.0
						/ exploitedValues/* pmax */
						* valueSet
								.elementAt((setSize - exploitedValues/* pmax */)
										+ n)
						* Math
								.cos(2.0 * Math.PI * p * n / exploitedValues/* pmax */);
				tempB = tempB
						+ 2.0
						/ exploitedValues/* pmax */
						* valueSet
								.elementAt((setSize - exploitedValues/* pmax */)
										+ n)
						* Math
								.sin(2.0 * Math.PI * p * n / exploitedValues/* pmax */);
			}

			double real = tempA / 2;
			double imm = -tempB / 2;
			double mod = Math.sqrt(real * real + imm * imm);
			// double ph=Math.atan(imm/real);

			// System.out.println(p+" mod: "+mod);
			/*
			 * if(p==0){ // RSSI too low // => // RSSI fluctuations primarily
			 * due to signal noise // instead of mobile node movements
			 * if(mod>=75)return -1; }
			 */
			if (p == harmonic) {
				return (float) mod;
			}
		}

		System.err.println("Unable to compute Fourier Coefficients");
		throw new Exception("Unable to compute Fourier Coefficients");
	}
}
